import os
import random
import time

import yaml
from eth_account import Account
from web3 import Web3
from web3.middleware.geth_poa import geth_poa_middleware


class Main:
    ABI = [
        {
            "inputs": [],
            "name": "marketByBNB",
            "outputs": [],
            "stateMutability": "payable",
            "type": "function",
        }
    ]

    def __init__(self) -> None:
        self.url = self.read_config().get("rpc")
        self.contract_address = self.read_config().get("contract_address")
        self.web3 = Web3(Web3.HTTPProvider(self.url))
        self.web3.middleware_onion.inject(geth_poa_middleware, layer=0)
        self.contract = self.web3.eth.contract(
            address=self.contract_address, abi=self.ABI
        )
        self.chain_id = self.read_config().get("chain_id")
        self.private_key = self.read_config().get("private_key")
        self.from_address = Account.from_key(self.private_key).address
        self.time_interval = str(self.read_config().get("time_interval")).split(",")
        self.number_of_accounts_created = str(
            self.read_config().get("number_of_accounts_created")
        ).split(",")
        self.quantity_range = str(self.read_config().get("quantity_range")).split(",")

    def send_transaction(self, transaction, private_key):
        try:
            signed_txn = self.web3.eth.account.sign_transaction(
                transaction, private_key=private_key
            )
            tx_hash = self.web3.eth.send_raw_transaction(signed_txn.rawTransaction)
            tx_receipt = self.web3.eth.wait_for_transaction_receipt(tx_hash)
            time.sleep(3)
            return tx_receipt
        except Exception as e:
            raise e

    def market_by_bnb(self, amount, from_address, private_key):
        nonce = self.web3.eth.get_transaction_count(from_address, "pending")
        estimated_gas = self.contract.functions.marketByBNB().estimate_gas(
            {
                "from": from_address,
                "value": self.web3.to_wei(amount, "ether"),
            }
        )
        gas_price = self.web3.to_wei(1, "gwei")
        transaction = self.contract.functions.marketByBNB().build_transaction(
            {
                "value": self.web3.to_wei(amount, "ether"),
                "gas": estimated_gas,
                "gasPrice": gas_price,
                "nonce": nonce,
                "chainId": self.chain_id,
            }
        )
        result = self.send_transaction(transaction, private_key)
        print(result)
        return result

    def bnb_transfer(
        self, from_address: str, to_address: str, private_key: str, amount: int = None
    ):
        try:
            gas_price = self.web3.to_wei(1, "gwei")
            if not amount:
                amount = self.web3.eth.get_balance(from_address)
                amount = amount - (gas_price * 21000)
            nonce = self.web3.eth.get_transaction_count(from_address, "pending")
            transaction = {
                "from": from_address,
                "to": to_address,
                "value": amount,
                "gas": 21000,
                "gasPrice": gas_price,
                "nonce": nonce,
                "chainId": self.chain_id,
            }
            result = self.send_transaction(transaction, private_key)
            return result
        except Exception as e:
            raise e

    def start(self):
        self.time_interval = random.randint(
            int(self.time_interval[0]), int(self.time_interval[1])
        )
        time.sleep(self.time_interval)
        self.number_of_accounts_created = random.randint(
            int(self.number_of_accounts_created[0]),
            int(self.number_of_accounts_created[1]),
        )
        self.quantity_range = random.uniform(
            float(self.quantity_range[0]), float(self.quantity_range[1])
        )
        for _ in range(1):
            acount = self.web3.eth.account.create()
            address, private_key = acount.address, acount.key.hex()
            print(address, private_key)
            balance = self.web3.eth.get_balance(self.from_address)
            amount = self.web3.to_wei(self.quantity_range + 0.04, "ether")
            if balance < amount:
                raise Exception(f"账户余额不足：{balance/10**18} BNB")
            self.bnb_transfer(self.from_address, address, self.private_key, amount)
            try:
                self.market_by_bnb(self.quantity_range, address, private_key)
            except Exception as e:
                print(e)
            finally:
                time.sleep(3)
                self.bnb_transfer(address, self.from_address, private_key)

    def read_config(self):
        with open("./config.yaml", "r", encoding="utf-8") as f:
            yaml_data = yaml.safe_load(f)
        return yaml_data


if __name__ == "__main__":
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    main = Main()
    main.start()
